﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Windows.Forms;
using System.Windows.Media.Animation;
using System.Drawing;
using System.Diagnostics;
using System.ComponentModel;
using System.Globalization;
using System.Resources;

using CK.Plugin;
using CK.Kernel.Config;
using CommonServices;
using ContribServices;
using AutoClick.WPFHelpers;
using AutoClick.Algorithms;

namespace AutoClick
{

    /// <summary>
    /// Plugin gestion of AutoClick
    /// </summary>
    [Plugin(Id = "{F5A81349-1B82-48f0-AAF8-9E6383316EBB}", PublicName = "EyeTracking AutoClick",
        Version = "1.0.0",
        DefaultPluginStatus = ConfigPluginStatus.Manual,
        Categories = new string[] { "Accessibility" },
        Description = "AutoClick for EyeTracking")]
    public class AutoClick : IPlugin, IAutoClick, INotifyPropertyChanged
    {
        #region Variables
        private bool _showGauge;
        private bool _showTypesOfClickMenu;
        private bool _showAreaOfReferenceClick;
        private int _progressionClickValue;
        private ResourceManager _resourceForText;
        private AutoClickCircleControl _areaOfReferenceClickControl;
        private ICommonTimer _commonTimer;
        private WPFAutoClickTypes _typeOfClickWindow;
        private IPointerDeviceDriver _pointerDeviceDriver;
        private AutoClickAlgorithmManager _autoClickAlgorithmManager;
        private WPFAutoClickGauge _gaugeOfClickProgression;
        private DoubleAnimation _areaOfReferenceClickAnimation;

        [Service(Requires = ServiceRequirements.MustExist)]
        public ICommonTimer Timer
        {
            get { return _commonTimer; }
            set { _commonTimer = value; }
        }
        [Service(Requires = ServiceRequirements.MustExist)]
        public IPointerDeviceDriver PointerDeviceDriver
        {
            get { return _pointerDeviceDriver; }
            set { _pointerDeviceDriver = value; }
        }
        public IPluginConfigAccessor Config { get; set; }
        #endregion

        #region Constructor
        private void Initialize()
        {
            //Add values on property bag for each fields if they are void
            Config.User.TryAdd("CommonTimerMultiple", 4);
            Config.User.TryAdd("Radius", 30);
            Config.User.TryAdd("ShowGauge", true);
            Config.User.TryAdd("ShowTypesOfClick", false);
            Config.User.TryAdd("ShowCircle", true);
            Config.User.TryAdd("DefaultTypeOfClick", "Left");
            Config.User.TryAdd("Algorithm", "PercentageCalculating");
            Config.User.TryAdd("CommonTimerInterval", Timer.Interval);

            //Set the language
            if (CultureInfo.CurrentUICulture.TwoLetterISOLanguageName == "fr")
                _resourceForText = new ResourceManager(typeof(TextFr));
            else
                _resourceForText = new ResourceManager(typeof(TextEn));

            //Initialize variables
            _showGauge = (bool)Config.User["ShowGauge"];
            _showTypesOfClickMenu = (bool)Config.User["ShowTypesOfClick"];
            _showAreaOfReferenceClick = (bool)Config.User["ShowCircle"];
            _areaOfReferenceClickControl = new AutoClickCircleControl(((string)Config.User["DefaultTypeOfClick"] != "noClick"));
            _areaOfReferenceClickAnimation = new DoubleAnimation();
            _areaOfReferenceClickAnimation.Duration = new System.Windows.Duration(TimeSpan.FromMilliseconds(Timer.Interval));
            _autoClickAlgorithmManager = new AutoClickAlgorithmManager();

            //Initialize algorithm
            UpdateAutoClickAlgorithm();
            _autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick = (string)Config.User["DefaultTypeOfClick"];
            _autoClickAlgorithmManager.Start();
            _autoClickAlgorithmManager.AutoClickAlgorithm.AutoClickCircleControl.ShowCircle = _showAreaOfReferenceClick;

            //initialize gauge
            InitializeGauge((string)Config.User["DefaultTypeOfClick"] != "noClick");

            //Lauch WPFHelpers if needed
            LaunchGaugeVisibility(_showGauge);
            LaunchChooseTypeOfClick(_showTypesOfClickMenu);
            LaunchCircleVisibility(_showAreaOfReferenceClick);

            //Register events
            Timer.IntervalChanged += new EventHandler(Timer_IntervalChanged);
            Timer.Tick += new EventHandler(Timer_Tick);
            _pointerDeviceDriver.PointerMove += new PointerDeviceEventHandler(PointerDeviceDriver_PointerMove);
        }
        #endregion

        #region Actions for each tick
        /// <summary>
        /// Called for each period of CommonTimer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Timer_Tick(object sender, EventArgs e)
        {
            if (_autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick != "noClick")
            {
                //Update the visibility of gauge
                if (_showGauge)
                {
                    if (_autoClickAlgorithmManager.AutoClickAlgorithm.ClickInProcess)
                        _gaugeOfClickProgression.Start();
                    if (!_autoClickAlgorithmManager.AutoClickAlgorithm.ClickInProcess)
                        _gaugeOfClickProgression.Stop();
                }

                //Update the progression's value of click and lauch the specific animation of gauge
                ProgressionValue = (int)Math.Round((double)((_autoClickAlgorithmManager.AutoClickAlgorithm.CountNumberOfCommonTimerTicks) * 100) / _autoClickAlgorithmManager.AutoClickAlgorithm.CommonTimerMultiple);
                _areaOfReferenceClickAnimation.From = (int)Math.Round((double)((_autoClickAlgorithmManager.AutoClickAlgorithm.CountNumberOfCommonTimerTicks) * 100) / _autoClickAlgorithmManager.AutoClickAlgorithm.CommonTimerMultiple);
                _areaOfReferenceClickAnimation.To = (int)Math.Round((double)((_autoClickAlgorithmManager.AutoClickAlgorithm.CountNumberOfCommonTimerTicks + 1) * 100) / _autoClickAlgorithmManager.AutoClickAlgorithm.CommonTimerMultiple);
                if (_gaugeOfClickProgression != null && _areaOfReferenceClickAnimation.To <= 100)
                    _gaugeOfClickProgression.progressBar1.BeginAnimation(System.Windows.Controls.ProgressBar.ValueProperty, _areaOfReferenceClickAnimation);
            }
        }
        #endregion

        #region Set parameters
        /// <summary>
        /// Initializes the gauge
        /// </summary>
        public void InitializeGauge(bool autoClickNeeded)
        {
            _gaugeOfClickProgression = new WPFAutoClickGauge(autoClickNeeded);
            _gaugeOfClickProgression.Show();
            _gaugeOfClickProgression.Topmost = true;
            _gaugeOfClickProgression.DataContext = this;
            if (autoClickNeeded)
                _gaugeOfClickProgression.Start();
            else
                _gaugeOfClickProgression.Stop();
        }
        /// <summary>
        /// Updates gauge's position if gauge is existing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void PointerDeviceDriver_PointerMove(object sender, PointerDeviceEventArgs e)
        {
            if (_gaugeOfClickProgression != null)
            {
                _gaugeOfClickProgression.PlaceGauge(e);
            }
        }
        /// <summary>
        /// Called when interval's commontimer has a new value
        /// Set a new duration for animation of gauge
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void Timer_IntervalChanged(object sender, EventArgs e)
        {
            _areaOfReferenceClickAnimation.Duration = new System.Windows.Duration(TimeSpan.FromMilliseconds(Timer.Interval));
            Config.User["CommonTimerInterval"] = Timer.Interval;
        }

        /// <summary>
        /// Called when the user changed the kind of click :
        /// -Set the new kind of click
        /// -Hide gauge and circle of click's reference if needed
        /// </summary>
        /// <param name="kindOfClick"></param>
        void TypeOfClick_NewSelection(String kindOfClick)
        {
            _autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick = kindOfClick;
            if (kindOfClick == "noClick")
            {
                _areaOfReferenceClickControl.AutoClickNeeded = false;
                _gaugeOfClickProgression.AutoClickNeeded = false;
                _areaOfReferenceClickControl.Stop();
                _gaugeOfClickProgression.Stop();
            }
            else
            {
                _areaOfReferenceClickControl.AutoClickNeeded = true;
                _gaugeOfClickProgression.AutoClickNeeded = true;
                if (_showAreaOfReferenceClick)
                    _areaOfReferenceClickControl.Start(_autoClickAlgorithmManager.AutoClickAlgorithm.AreaOfReferenceClickRadius*2);
                if (_showGauge)
                    _gaugeOfClickProgression.Start();
            }
        }

        /// <summary>
        /// Called when user changes the algorithm
        /// </summary>
        private void UpdateAutoClickAlgorithm()
        {
            //Initialize parameters needed to lauch an algorithm (kind of click, radius, commontimer's multiple)
            string typeOfClick;
            if (_autoClickAlgorithmManager.AutoClickAlgorithm != null)
                typeOfClick = _autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick;
            else
                typeOfClick = (string)Config.User["DefaultTypeOfClick"];
            int radius = (int)Config.User["Radius"];
            int commonTimerMultiple = (int)Config.User["CommonTimerMultiple"];

            //Change the algorithm
            string autoClickAlgorithmName = (string)Config.User["Algorithm"];
            switch (autoClickAlgorithmName)
	        {
                case "PercentageCalculating":
                    _autoClickAlgorithmManager.AutoClickAlgorithm = new AlgorithmPercentageCalculating(commonTimerMultiple, radius, Timer, PointerDeviceDriver, typeOfClick, _areaOfReferenceClickControl);
                    break;
                case "ClickIfPointerStayInRadius":
                    _autoClickAlgorithmManager.AutoClickAlgorithm = new AlgorithmClickIfPointerStayInRadius(commonTimerMultiple, radius, Timer, PointerDeviceDriver, typeOfClick, _areaOfReferenceClickControl);
                    break;
                case "ClickIfPointerIsInRadius":
                    _autoClickAlgorithmManager.AutoClickAlgorithm = new AlgorithmClickIfPointerIsInRadius(commonTimerMultiple, radius, Timer, PointerDeviceDriver, typeOfClick, _areaOfReferenceClickControl);
                    break;
                default: break;
	        }
        }

        /// <summary>
        /// Called when user changes parameters from editor
        /// </summary>
        /// <param name="e"></param>
        public void OnConfigurationChanged(IConfigEntry e)
        {
            if (_autoClickAlgorithmManager != null)
            {
                switch (e.Key)
                {
                    //Hide or show gauge
                    case "ShowGauge":
                        _showGauge = (bool)Config.User["ShowGauge"];
                        LaunchGaugeVisibility(_showGauge);
                        break;
                    //Hide or show types of click menu
                    case "ShowTypesOfClick":
                        _showTypesOfClickMenu = (bool)Config.User["ShowTypesOfClick"];
                        LaunchChooseTypeOfClick(_showTypesOfClickMenu);
                        break;
                    //Hide or show circle of reference
                    case "ShowCircle":
                        _showAreaOfReferenceClick = (bool)Config.User["ShowCircle"];
                        LaunchCircleVisibility(_showAreaOfReferenceClick);
                        _autoClickAlgorithmManager.AutoClickAlgorithm.AutoClickCircleControl.ShowCircle = _showAreaOfReferenceClick;
                        break;
                    //Updates radius of reference's circle
                    case "Radius":
                        _autoClickAlgorithmManager.AutoClickAlgorithm.AreaOfReferenceClickRadius = (int)Config.User["Radius"];
                        _autoClickAlgorithmManager.AutoClickAlgorithm.AutoClickCircleControl.UpdateCircleRadius
                            (_autoClickAlgorithmManager.AutoClickAlgorithm.AreaOfReferenceClickRadius*2, _autoClickAlgorithmManager.AutoClickAlgorithm.ReferencialPointerPosition);
                        break;
                    //Updates common timer's multiple
                    case "CommonTimerMultiple":
                        _autoClickAlgorithmManager.AutoClickAlgorithm.CommonTimerMultiple = (int)Config.User["CommonTimerMultiple"];
                        break;
                    //Updates algorithm
                    case "Algorithm":
                        LaunchCircleVisibility(false);
                        UpdateAutoClickAlgorithm();
                        _autoClickAlgorithmManager.Start();
                        LaunchCircleVisibility(_showAreaOfReferenceClick);
                        break;
                    default : break;
                }
            }
        }
        #endregion

        #region Start or stop WPFHelpers
        /// <summary>
        /// Start or stop circle of click's reference
        /// </summary>
        /// <param name="mustBeLaunch"></param>
        public void LaunchCircleVisibility(bool mustBeLaunch)
        {
            if (_autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick != "noClick")
            {
                if (mustBeLaunch)
                {
                    _autoClickAlgorithmManager.AutoClickAlgorithm.AutoClickCircleControl.Start(_autoClickAlgorithmManager.AutoClickAlgorithm.AreaOfReferenceClickRadius*2);
                }
                else
                {
                    _autoClickAlgorithmManager.AutoClickAlgorithm.AutoClickCircleControl.Stop();
                }
            }
        }
        /// <summary>
        /// Start or stop gauge progression of autoclick
        /// </summary>
        /// <param name="mustBeLaunch"></param>
        public void LaunchGaugeVisibility(bool mustBeLaunch)
        {
            if (_autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick != "noClick")
            {
                if (mustBeLaunch)
                {
                    _gaugeOfClickProgression.Start();
                }
                else
                {
                    _gaugeOfClickProgression.Stop();
                }
            }
        }
        /// <summary>
        /// Start or stop visibility of the menu of types of click
        /// </summary>
        /// <param name="mustBeLaunch"></param>
        public void LaunchChooseTypeOfClick(bool mustBeLaunch)
        {
            if (mustBeLaunch && _typeOfClickWindow == null)
            {
                _typeOfClickWindow = new WPFAutoClickTypes(_autoClickAlgorithmManager.AutoClickAlgorithm.TypeOfClick, _resourceForText);
                _typeOfClickWindow.Show();
                _typeOfClickWindow.Visibility = System.Windows.Visibility.Visible;
                _typeOfClickWindow.Topmost = true;
                _typeOfClickWindow.NewSelection += new WPFAutoClickTypes.NewTypeOfClickSelected(TypeOfClick_NewSelection);
            }
            if (!mustBeLaunch && _typeOfClickWindow != null)
            {
                _typeOfClickWindow.NewSelection -= new WPFAutoClickTypes.NewTypeOfClickSelected(TypeOfClick_NewSelection);
                _typeOfClickWindow.Visibility = System.Windows.Visibility.Collapsed;
                _typeOfClickWindow = null;
            }
        }
        #endregion

        #region IPlugin Membres
        public bool CanStart(out string lastError)
        {
            lastError = "";
            return true;
        }
        public bool Setup(ISetupInfo info)
        {
            return true;
        }
        public void Start()
        {
            Initialize();
        }
        public void Stop()
        {
            //Stop algorithm and all WPFhelpers's visibility
            _autoClickAlgorithmManager.Stop();
            LaunchChooseTypeOfClick(false);
            LaunchGaugeVisibility(false);
            LaunchCircleVisibility(false);

            //Stop events's subscribtion
            Timer.IntervalChanged -= new EventHandler(Timer_IntervalChanged);
            Timer.Tick -= new EventHandler(Timer_Tick);
            _pointerDeviceDriver.PointerMove -= new PointerDeviceEventHandler(PointerDeviceDriver_PointerMove);
        }
        public void Teardown()
        {
        }
        #endregion

        #region IAutoClick Membres
        public int ProgressionValue
        {
            get { return _progressionClickValue; }
            set 
            {
                _progressionClickValue = value;
                OnNotifyPropertyChanged("ProgressionValue"); 
            }
        }
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// Lauch updates when property bag's datas change
        /// </summary>
        /// <param name="propertyName"></param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion 
    }
}
